﻿// Collection.cpp : 实现文件
//

#include "stdafx.h"
#include "GHCY.h"
#include "Ground.h"
#include "../Hui/Hui.h"
#include "Car.h"
#include "GException.h"
#include "LogList.h"
#include "Collection.h"

// Collection

#ifdef _DEBUG
	#define new DEBUG_NEW
#endif

/// <作者>Mr. Hu</作者>
/// <日期>202001009</日期>
/// <功能>同类端点集合类</功能>
/// <参数 name = ""></参数>
/// <返回></返回>
/// <说明>同类端点集合，用于神经网络计算</说明>
Collection::Collection()
	: m_pNextColl(0)
	, m_pPreColl(0)
	, m_pPickupPoint(0)
	, m_pStorePoint(0)
	//, m_pointType(0)
	// 成员修改后还要改operator =
{
	RtlZeroMemory(m_bufCode, sizeof(m_bufCode));

	m_arrPoint.SetSize(0, 20);
}

/// <作者>Mr. Hu</作者>
/// <日期>202001031</日期>
/// <功能>重载=</功能>
/// <参数 name = "coll">指定的Collection</参数>
/// <返回>Collection</返回>
/// <说明>复制Collection对象</说明>
//Collection& Collection::operator =(const Collection& /*coll*/)
//{
//	// 这时只生成一个空Collection对象
//	return *this;
//}

Collection::~Collection()
{
	// 不在这里删除
	//for (size_t i = 0, length = m_arrPoint.GetCount(); i < length; i++)
	//{
	//	delete m_arrPoint[i];
	//}
}

/// <作者>Mr. Hu</作者>
/// <日期>202001009</日期>
/// <功能>添加一个端点</功能>
/// <参数 name = "pep">指向端点</参数>
/// <返回></返回>
/// <说明>端点总数</说明>
INT_PTR Collection::add(EndPoint* pep)
{
	return m_arrPoint.Add(pep);
}

/// <作者>Mr. Hu</作者>
/// <日期>202001009</日期>
/// <功能>取端点数量</功能>
/// <参数 name = ""></参数>
/// <返回>端点总数</返回>
/// <说明></说明>
INT_PTR Collection::getSize()
{
	return m_arrPoint.GetCount();
}

/// <作者>Mr. Hu</作者>
/// <日期>202001010</日期>
/// <功能>计算优先级</功能>
/// <参数 name = ""></参数>
/// <返回></返回>
/// <说明></说明>
void Collection::updatePriority(Car* pCar)
{
	size_t length = m_arrPoint.GetCount();
	if (!length)
	{
		m_priority.p = 0;	// 集合没有项，优先级为0，最低
		return;
	}

	// 允许存取货
	size_t i = 0;
	m_pPickupPoint = 0;	// 取货点
	m_pStorePoint = 0;	// 存货点
	//BOOL bFull = TRUE;	// 全部工位都是成品
	//BOOL bEmpty = TRUE;	// 全部工位都空
	COleDateTime dtc = COleDateTime::GetCurrentTime();
	COleDateTimeSpan dts = 0;	// 等待时间
	for (; i < length; i++)
	{
		EndPoint* pep = m_arrPoint[i];
		//int r = pep->allowPickup(0);
		int r = pep->allowPickup(pCar);		// 20210516 参数改为pCar，能够从当前点取货。
		switch (r)
		{
		case 0:
			//bFull = FALSE;			// 不全是成品
			break;

		case 1:
			if(m_priority.p1 > 0)	// 原来是0不减
				m_priority.p1--;	// 有AGV正在取货，优先级-1


			// 20201212
			//if(m_bufCode[0] == L'A')		// ???? Axxx只能一个取货
			//	m_priority.p1 = 0;
			
			
			break;

		case 2:
			m_pPickupPoint = pep;	// 取货点
			//if (pep->m_nType == 5)
			//	m_priority.p1++;	// 电梯允许取货时优先级加1

			// 来的点优先，移到Collections::updatePriority中
			//if (pCar->m_arrPath.GetCount()
			//	&& pCar->m_arrPath[0].m_nCard == (pep->getRoadNode())->m_nCard
			//	&& pep->isProduct()
			//	)
			//{
			//	m_priority.p1++;	// 来的点优先
			//}
			break;

		default:
			ASSERT(FALSE);
			break;
		}

		// 允许存货
		//int num = m_pPreColl ? m_pPreColl->m_nMinLayer : 0;	// 取上一集合的最小层数
		if ( /*num &&*/ pep->allowStore(pCar))	// num为0表示没有货
		{
			if (m_nPreStore)
				m_nPreStore--;	// 先前去预存的数
			else
				m_pStorePoint = pep;	// 存货点
		}
		//else
		//	nEmpty = FALSE;			// 不全空

		// 等待时间
		COleDateTimeSpan sp = dtc.m_dt - pep->m_dtWait.m_dt;
		if (sp > dts)
			dts = sp;
	}

	if (!m_pStorePoint)		// 如果不能存货
	{
		// ?? 20210414 潜伏2t项目，不清上一级
		//if (m_pPreColl)
		//	m_pPreColl->m_priority.p1 = 0;	// 上级取货优先级为0

		if (m_pPickupPoint && length > 1 && m_priority.p1 > 0)	// 可以取货，1个工位时，如电梯，不加优先级，原来是0也不加
			m_priority.p1++;				// 本集合取货优先级加1
	}

	if (!m_pPickupPoint)	// 如果不能取货
	{
		m_priority.p1 = 0;	// 优先级为0

		if (m_pPreColl						// 有上一级
			&& m_pPreColl->m_priority.p1	// 上级原来是0，不加
			&& length > 1					// 本级只有一个工位，如电梯，不加上一级
			)
			m_pPreColl->m_priority.p1++;	// 上级取货优先级加1

		//if (m_pStorePoint)	// 可以取货
		//	m_priority.p1++;				// 本集合取货优先级加1
	}








	// 次优先级，等待时间长的优先，暂时未用
	double ts = dts.GetTotalSeconds();
	m_priority.p2 = ts > MAXINT32 ? MAXINT32 : (INT32)ts;









	// ???? 临时用
	// 与其它相反，在线充电被占时，允许继取上级货
	if (*(UINT64*)m_bufCode == *(UINT64*)L"R301" && !m_pStorePoint)		// 如果不能存货
	{
		if (m_pPreColl)
			m_pPreColl->m_priority.p1 = 20;	// 上级取货优先级为20
	}

	// ???? 临时用
	// 20210517 演示程序，不用这一段
	// 20210814 坚美不用这一句
	//if (m_bufCode[0] == L'A')
	//{
	//	updatePriorityA(pCar);
	//	return;
	//}



}






/// <作者>Mr. Hu</作者>
/// <日期>202001014</日期>
/// <功能>重写计算优先级</功能>
/// <参数 name = "pCar">指定的AGV</参数>
/// <返回></返回>
/// <说明></说明>
void Collection::updatePriorityA(Car* pCar)
{
	// 未用基类优先级计算

	size_t nPoint = m_arrPoint.GetCount();
	if (!nPoint)
	{
		m_priority.p = 0;	// 集合没有项，优先级为0，最低
		return;
	}



	// ???? 20201215 临时
	// 上级计算不能存不能取，空车到充电点等
	if (m_pPickupPoint == 0	// 取货点
		&& m_pStorePoint == 0	// 存货点
		)
	{
		if (m_pPreColl
			&& m_pPreColl->m_pPreColl
			)
		{
			// 20210723 注释，否则有AGV在1号取货时，其它AGV会到充电点
			//m_pPreColl->m_priority.p1 = m_pPreColl->m_priority0.p1;					// 到充电点

			if (m_bufCode[1] == L'1')	// ???? 临时用，只有下砖（A1）执行下面的判断，电梯不用
			{
				// 如果有充电，先取货。没有充电，直接充电
				// ???? 要求充电先初始化，以后改多次循环解决这个问题
				if (m_pPreColl->m_pStorePoint)
					m_pPreColl->m_pPreColl->m_priority.p1 = 0;		// 不取空托
				else if(m_pPreColl->m_pPreColl->m_priority.p1 < 50)
					m_pPreColl->m_pPreColl->m_priority.p1 = 50;		// 取空托
			}
			//return;
		}
	}




	// 存取货
	//m_pPickupPoint = 0;	// 取货点
	//m_pStorePoint = 0;	// 存货点

	// 计算集合内有没有AGV
	CArray<Car*>& arrCars = theApp.m_pGround->m_arrCars;
	size_t nCars = arrCars.GetCount();
	BOOL nHasAGV = FALSE;
	for (size_t j = 0; j < nCars; j++)
	{
		Car* pc = arrCars.GetAt(j);
		if (pc->getDisable())
			continue;	// 停用

		if (!pc->m_pEndPoint)
			continue;	// 没有终点

		// 判断AGV终点是否在集合内
		for (size_t i = 0; i < nPoint; i++)
		{
			if (pc->m_pEndPoint == m_arrPoint[i])
			{
				if (pc == pCar)
					nHasAGV = FALSE;	// 排除自己
				else
				{
					// 有AGV正在进入集合，权限设0
					m_priority.p1 = 0;

					nHasAGV = TRUE;		// 有AGV
				}

				break;;
			}
		}

		// 有AGV正在进入集合，不允许外部存取货
		//if (m_priority.p1 == 0)
		if (nHasAGV)
		{
			m_pPickupPoint = 0;	// 取货点
			m_pStorePoint = 0;	// 存货点

			// Axxx有AGV时，先取空托 // ???? 专用
			if (m_pPreColl
				&& m_pPreColl->m_pPreColl
				&& m_pPreColl->m_pPreColl->m_priority.p1 < 50
				&& (m_bufCode[1] == L'1')	// ???? 临时用，只有下砖（A1）执行下面的语句，电梯不用
				)
			{
				m_pPreColl->m_pPreColl->m_priority.p1 = 50;		// 取空托
			}


			break;
		}
	}

	// 如果允许存空托，先取空托，不去拉实托
	// 只有下砖区用这条规则，电梯区不用
	if (m_pStorePoint && m_bufCode[1] == L'1')
	{
		m_priority.p1 = 0;
		m_pPickupPoint = 0;
	}

	// 次优先级，等待时间长的优先，暂时未用
	//double ts = dts.GetTotalSeconds();
	//m_priority.p2 = ts > MAXINT32 ? MAXINT32 : (INT32)ts;
}


















/// <作者>Mr. Hu</作者>
/// <日期>202001011</日期>
/// <功能>在集合中查找相同卡号的端点</功能>
/// <参数 name = "prn">指定的道路节点</参数>
/// <返回>TRUE 有相同点，FALSE 没有相同点</返回>
/// <说明></说明>
EndPoint* Collection::sameCard(RoadNode* prn)
{
	for (size_t i = 0, length = m_arrPoint.GetCount(); i < length; i++)
	{
		EndPoint* pep = m_arrPoint[i];
		if (pep->getRoadNode()->m_nCard == prn->m_nCard)
			return pep;
	}
	return 0;
}

/// <作者>Mr. Hu</作者>
/// <日期>202001101</日期>
/// <功能>找最短距离</功能>
/// <参数 name = "pCar">指定的AGV</参数>
/// <参数 name = "dis">返回AGV到端点的距离</参数>
/// <返回>找到的端点</返回>
/// <说明>遍历集合内所有端点，取最短的可取货端点</说明>
EndPoint* Collection::getPickupPoint(Car* pCar, int& /*dis0*/)
{
	// 已经充电的AGV不再去充电
	if (pCar
		&& pCar->m_pEndPoint
		&& pCar->m_pEndPoint->m_bRecharge		// AGV在充电
		&& ((EndPoint::PointType&)m_pointType).m_bRecharge					// 当前组是充电
		&& pCar->m_carData.bRecharge //(pCar->m_pReadData->nState & 0x00f0)								// 正在充电
		//&& (pCar->m_pReadData->nState & 0x000f) == 0x0005					// 正在充电
		)
	{
		return 0;
	}

	for (size_t i = 0, length = m_arrPoint.GetCount(); i < length; i++)
	{
		EndPoint* pep = m_arrPoint[i];

		if (pep->allowPickup(pCar) == 2)
			return pep;	// 不允许取货，跳过
	}
	return 0;
	/*
	dis0 = MAXINT;
	EndPoint* pep0 = 0;
	for (size_t i = 0, length = m_arrPoint.GetCount(); i < length; i++)
	{
		EndPoint* pep = m_arrPoint[i];

		if (pep->allowPickup(pCar) != 2)
			continue;	// 不允许取货，跳过

		//RoadNode* prn = pep->getRoadNode();
		//int dis = prn->calDis(pCar->m_nX, pCar->m_nY);
		//if (dis < dis0)
		//{
		//	dis0 = dis;
		//	pep0 = pep;
		//}
		// 20220221 改成用A*计算
		TRY
		{
			ASSERT(pCar);

			int len = (int)pCar->m_arrPath.GetCount() - 1;
			//ASSERT(len >= 0);

			// 20220312 发第一个在充电，第二个发车点不存在时，len是0
			if (len < 0)
				continue;

			// 20220308 如果起点和终点是同一点(发车时取发车第二点，或存完货又在同一点取货)，用A*算法出错。
			int nn0 = pCar->m_arrPath.GetAt(len).m_nID - 1;		// 路径起点
			int nn1 = pep->m_pRoadNode->m_nID - 1;				// 路径终点
			int dis = 0;										// 起点到终点的路径距离
			CArray<RoadNode, RoadNode&> & arrRoadNodes = theApp.m_pGround->m_arrRoadNodes;	// 道路节点表
			if (arrRoadNodes[nn0].m_nCard != arrRoadNodes[nn1].m_nCard)						// 如果起点不同，用A*计算距离，否则算0
			{
				AStar aStar(arrRoadNodes);
				aStar.getPath(nn0, nn1);
				dis = aStar.m_nG;
			}
			else
			{
				// 如果是取当前点，取当前的距离
				dis = pCar->m_pReadData->nDistance;
				if (dis < 0)
					dis = -dis;
			}

			//tc.getCounter();	// 0.1ms

			if (dis < dis0)
			{
				dis0 = dis;
				pep0 = pep;
			}
		}
		CATCH_ALL(e)	// ?? 分情况进行异常处理
		{
			//e->ReportError();		// 不能写日志，不显示信息
		}
		END_CATCH_ALL
	}

	return pep0;
	*/
}

/// <作者>Mr. Hu</作者>
/// <日期>202001116</日期>
/// <功能>初始化</功能>
/// <参数 name = ""></参数>
/// <返回></返回>
/// <说明>遍历集合内所有端点，初始化参数
///		如果集合内有货，m_nMinLayer等于最少的有货层数
///		如果集合内没有货，m_nMinLayer = 0
/// </说明>
void Collection::init()
{
	// 计算最大最小层数
	// 如果集合内有货，m_nMinLayer等于最少的有货层数
	// 如果集合内没有货，m_nMinLayer = 0
	m_nMaxLayer = 0;
	m_nMinLayer = MAXINT;
	m_priority.p = m_priority0.p;	// 优先级复位
	m_nPreStore = 0;				// 预存数复位

	for (size_t i = 0, length = m_arrPoint.GetCount(); i < length; i++)
	{
		EndPoint* pep = m_arrPoint[i];

		// 20201213 先计算m_nZNumber
		pep->getZNum();

		if (m_nMaxLayer < pep->m_nZCurrent)
			m_nMaxLayer = pep->m_nZCurrent;
		if (pep->m_nZCurrent && m_nMinLayer > pep->m_nZCurrent)
			m_nMinLayer = pep->m_nZCurrent;
	}
	if (!m_nMaxLayer)
		m_nMinLayer = 0;
	ASSERT(m_nMaxLayer >= m_nMinLayer);
}


/// <作者>Mr. Hu</作者>
/// <日期>202001118</日期>
/// <功能>初始化</功能>
/// <参数 name = ""></参数>
/// <返回></返回>
/// <说明>计算取货数量对下一级的影响
/// </说明>
void Collection::init2()
{
	if (!m_pNextColl)
		return;	// 没有下一级

	// 计算取货数，存到下级集合中
	// 判断有没有正在当前道取货的AGV
	// ???? 嵌套遍历，到这里已经几层了，严重影响效率
	//CArray<Car*>& arrCars = theApp.m_pGround->m_arrCars;
	//size_t nCars = arrCars.GetCount();
	//for (size_t i = 0; i < nCars; i++)
	//{
	//	Car* pc = arrCars.GetAt(i);
	//	if (pc->getDisable())
	//		continue;	// 停用

	//	HLock hl(&pc->m_mutexPath, 1, (DWORD)-13);	// 等待读，退出时自动解锁
	//	size_t nPath = pc->m_arrPath.GetCount();
	//	if (pc->m_bLoaded							// 有货
	//		&& !(pc->m_pReadData->nState == 0x1045	// 排除取货刚到时的过渡阶段
	//			//&& pc->m_arrPath.GetCount() == 2	// 20201122 端点等待不是2段
	//			&& pc->m_arrPath[nPath - 1].m_nCard < 0
	//			)
	//		)
	//	{
	//		continue;
	//	}

	//	if (GETBYTE(pc->m_pReadData->nState, 0) == 0x85)	// 只取低位，高位可能有不同的数
	//		continue;	// 存货完成，不算占用
	//	
	//	EndPoint* pep = pc->m_pEndPoint;
	//	if (!pep)
	//		continue;

	//	// 20210716 坚美雅洁专用，有AGV取1号点时，其它AGV不去充电
	//	ASSERT(pep);		// pep是AGV的终点
	//	if ((*(UINT64*)pep->m_bufCode == *(UINT64*)L"A302")		// 1号点，A302
	//		&& (*(UINT64*)m_bufCode == *(UINT64*)L"R301")		// 当前是充电集合
	//		)
	//	{
	//		m_priority.p = 0;									// 优先级为0，不允许充电任务
	//	}

	//	if (pep->m_pColl != this)
	//		continue;

	//	m_pNextColl->m_nPreStore++;
	//}
}
